﻿using System.Collections.Generic;

namespace Aeon.Emulator
{
    /// <summary>
    /// Defines an interrupt handler for a virtual machine.
    /// </summary>
    public interface IInterruptHandler : IVirtualDevice
    {
        #region Properties
        /// <summary>
        /// Gets the interrupts which are handled by this handler.
        /// </summary>
        IEnumerable<InterruptHandlerInfo> HandledInterrupts { get; }
        #endregion

        #region Methods
        /// <summary>
        /// Called when the interrupt handler should perform its action.
        /// </summary>
        /// <param name="interrupt">Raised interrupt number.</param>
        void HandleInterrupt(int interrupt);
        #endregion
    }

    /// <summary>
    /// Describes an interrupt handled by a virtual device.
    /// </summary>
    public struct InterruptHandlerInfo
    {
        #region Constructors
        /// <summary>
        /// Initializes a new InterruptHandlerInfo struct.
        /// </summary>
        /// <param name="interrupt">Interrupt which is handled.</param>
        public InterruptHandlerInfo(int interrupt)
        {
            this.interrupt = interrupt;
            this.registers = Registers.None;
            this.hookable = false;
            this.clearInterruptFlag = false;
        }
        /// <summary>
        /// Initializes a new InterruptHandlerInfo struct.
        /// </summary>
        /// <param name="interrupt">Interrupt which is handled.</param>
        /// <param name="savedRegisters">Registers to be saved before the handler is invoked.</param>
        public InterruptHandlerInfo(int interrupt, Registers savedRegisters)
        {
            this.interrupt = interrupt;
            this.registers = savedRegisters;
            this.hookable = false;
            this.clearInterruptFlag = false;
        }
        /// <summary>
        /// Initializes a new InterruptHandlerInfo struct.
        /// </summary>
        /// <param name="interrupt">Interrupt which is handled.</param>
        /// <param name="savedRegisters">Registers to be saved before the handler is invoked.</param>
        /// <param name="isHookable">Value indicating whether the interrupt handler is hookable.</param>
        /// <param name="clearInterruptFlag">Value indicating whether the interrupt handler should clear the CPU Interrupt Enable flag.</param>
        public InterruptHandlerInfo(int interrupt, Registers savedRegisters, bool isHookable, bool clearInterruptFlag)
        {
            this.interrupt = interrupt;
            this.registers = savedRegisters;
            this.hookable = isHookable;
            this.clearInterruptFlag = clearInterruptFlag;
        }
        /// <summary>
        /// Initializes a new InterruptHandlerInfo struct.
        /// </summary>
        /// <param name="interrupt">Interrupt which is handled.</param>
        /// <param name="isHookable">Value indicating whether the interrupt handler is hookable.</param>
        public InterruptHandlerInfo(int interrupt, bool isHookable)
        {
            this.interrupt = interrupt;
            this.registers = Registers.None;
            this.hookable = isHookable;
            this.clearInterruptFlag = false;
        }
        /// <summary>
        /// Initializes a new InterruptHandlerInfo struct.
        /// </summary>
        /// <param name="interrupt">Interrupt which is handled.</param>
        /// <param name="isHookable">Value indicating whether the interrupt handler is hookable.</param>
        /// <param name="clearInterruptFlag">Value indicating whether the interrupt handler should clear the CPU Interrupt Enable flag.</param>
        public InterruptHandlerInfo(int interrupt, bool isHookable, bool clearInterruptFlag)
        {
            this.interrupt = interrupt;
            this.registers = Registers.None;
            this.hookable = isHookable;
            this.clearInterruptFlag = clearInterruptFlag;
        }
        #endregion

        #region Operators
        /// <summary>
        /// Returns an InterruptHandlerInfo struct for an interrupt.
        /// </summary>
        /// <param name="interrupt">Interrupt to wrap with the struct.</param>
        /// <returns>InterruptHandlerInfo struct for the interrupt.</returns>
        public static implicit operator InterruptHandlerInfo(int interrupt)
        {
            return new InterruptHandlerInfo(interrupt);
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets or sets the handled interrupt.
        /// </summary>
        public int Interrupt
        {
            get { return interrupt; }
            set { interrupt = value; }
        }
        /// <summary>
        /// Gets or sets the registers to be saved before the handler is invoked.
        /// </summary>
        public Registers SavedRegisters
        {
            get { return registers; }
            set { registers = value; }
        }
        /// <summary>
        /// Gets or sets a value indicating whether the interrupt handler is hookable.
        /// </summary>
        public bool IsHookable
        {
            get { return hookable; }
            set { hookable = value; }
        }
        /// <summary>
        /// Gets or sets a value indicating whether the CPU Interrupt Enable flag should be cleared by the interrupt handler.
        /// </summary>
        public bool ClearInterruptFlag
        {
            get { return clearInterruptFlag; }
            set { clearInterruptFlag = value; }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Gets a string representation of the InterruptHandlerInfo struct.
        /// </summary>
        /// <returns>String representation of the InterruptHandlerInfo struct.</returns>
        public override string ToString()
        {
            return string.Format("int {0:X2}h, {1}", interrupt, registers);
        }
        #endregion

        #region Private Fields
        private int interrupt;
        private Registers registers;
        private bool hookable;
        private bool clearInterruptFlag;
        #endregion
    }
}
